#include "logdatabase.h"

#define DB_NAME _T("//log.xdb")

CLogDataBaseImpl::CLogDataBaseImpl()
{

}
CLogDataBaseImpl::~CLogDataBaseImpl()
{
	logi("CLogDataBaseImpl::~CLogDataBaseImpl");
}
std_method_impl CLogDataBaseImpl::Init(int Argc, basic_tchar* Argv[], IBase* pRot, IBase* pBase)
{
	rc_assert(pRot != NULL, E_FAIL)

	HRESULT hr = S_OK;
	hr = pRot->QueryInterface(IID_IComRotMessage, (void**)&m_pRot);
	rc_assert(hr == S_OK, E_FAIL)

	basic_tstring strDBPath = GetExePathString();
	strDBPath += DB_NAME;
	logi("db_name %s", strDBPath.c_str());


	int rc = FileIsExist(strDBPath.c_str());
	rc_assert_log(rc == S_SUCCESS, S_OK, "FileIsExist faild")

#if (TARGET_OS == OS_WINDOWS)
	CharPtr pBuf;
	pBuf = ucs2_to_char(strDBPath.c_str(), s_wstrlen(strDBPath.c_str()));
	hr = m_db.Connect(pBuf);
#elif (TARGET_OS == OS_POSIX)
	hr = m_db.Connect(strDBPath.c_str());
#elif (TARGET_OS == OS_DARWIN)
	hr = m_db.Connect(strDBPath.c_str());
#endif 

	if (S_FAILED(hr))
	{
		loge("Open database is error");
		OpenErrorLogDataBase();
	}
	else
	{
		loge("Open database is ok");
	}


	return S_OK;
}
std_method_impl CLogDataBaseImpl::Uninit()
{
	return S_OK;
}
std_method_impl CLogDataBaseImpl::Start(_pinstance hInstance, UINT uType)
{

	HRESULT hr = S_OK;


	return S_OK;
}
std_method_impl CLogDataBaseImpl::Stop(UINT uExitCode)
{
	return S_OK;
}
std_method_impl CLogDataBaseImpl::IsTableExist(LPCSTR name, IBase **pTable)
{
	MapTableStructIterator it = m_pMapTableStruct.find(name);
	rc_assert(m_pMapTableStruct.end() != it, E_FAIL)
	return it->second->QueryInterface(IID_ITableStruct, (void**)pTable);
}
std_method_impl CLogDataBaseImpl::SyncTable(LPCSTR name, IBase *pBase)
{
	HRESULT hr = S_OK;

	MapTableStructIterator it = m_pMapTableStruct.find(name);
	rc_assert((m_pMapTableStruct.end() == it), E_FAIL);

	ITableStruct* pITableStruct = NULL;
	hr = pBase->QueryInterface(IID_ITableStruct, (void**)&pITableStruct);
	rc_assert(hr == S_OK, E_FAIL)

	UINT Exist = S_ERROR;


	hr = m_db.IsTableExist(name, Exist);
	rc_assert(hr == S_OK, E_FAIL)

	hr = ((Exist == S_SUCCESS) ?
		UpdateTable(name, pITableStruct) :
		CreateTable(name, pITableStruct));

	rc_assert_log(hr == S_OK, E_FAIL, "syntable_sql==>Field==>faild")

	m_pMapTableStruct[name] = pITableStruct;

	return S_OK;
}
std_method_impl CLogDataBaseImpl::InsertTable(UCHAR* pBuf, ULONG uSize)
{
	HRESULT		hr = S_OK;

	JsonSafePtr	pJsonRoot = cJSON_Parse((char*)pBuf);
	rc_assert(IsJsonObject(pJsonRoot.m_p), E_FAIL);

	cJSON* pJsonTable = cJSON_GetObjectItem(pJsonRoot, Rule_Table);
	rc_assert(IsJsonString(pJsonTable), E_FAIL);

	_lComPtr<ITableStruct> pTableStruct;
	pTableStruct.dispose();

	hr = this->IsTableExist(pJsonTable->valuestring,(IBase**)&pTableStruct.m_p);
	rc_assert(hr == S_OK, E_FAIL)

	UINT op = pTableStruct->GetOp();

	if (TABLE_OP_UPDATE == op)
	{
		Update(pJsonTable->valuestring, pJsonRoot, pTableStruct);
	} 
	else if (TABLE_OP_INSERT == op)
	{
		Insert(pJsonTable->valuestring, pJsonRoot, pTableStruct);
	} 
	else if (TABLE_OP_REFRESH == op)
	{
		Refresh(pJsonTable->valuestring, pJsonRoot, pTableStruct);
	}

	return S_OK;
}
std_method_impl CLogDataBaseImpl::OpenErrorLogDataBase()
{
	return S_OK;
}

std_method_impl CLogDataBaseImpl::UpdateTable(LPCSTR name,
	ITableStruct *pITableStruct)
{

	//logi("alert==>");
	HRESULT hr = S_OK;
	vector<string>	vecfield;
	hr = m_db.GetTableStruct(name, vecfield);

	Table table;
	UINT uFieldCount = 0;

	pITableStruct->GetFieldsCount(uFieldCount);
	rc_assert_log(uFieldCount > 0, E_FAIL, "Table_Field_Zero")

	table.resize(uFieldCount);

	hr = pITableStruct->GetAllRecord(&table[0], uFieldCount);
	rc_assert(hr == S_OK, E_FAIL)

	UINT uloop = 0;

	Table AlertTable;

	for (uloop = 0; uloop < uFieldCount; uloop++)
	{
		FieldSturct& field = table[uloop];

		UINT uIndex = 0;
		UINT uFind = 0;
		for (uIndex = 0; uIndex < vecfield.size(); uIndex++)
		{
			if (s_stricmp(field.name,vecfield[uIndex].c_str()) == 0) {
				uFind = 1;
				break;
			}
		}
		if (uFind == 0) {
			AlertTable.push_back(field);
		}
	}

	//logw("table_name:%s fcount:%u ==>rcount:%u", name, vecfield.size(), uFieldCount);

	rc_assert(AlertTable.size() > 0,S_OK)

	for (uloop = 0; uloop < AlertTable.size(); uloop++)
	{
		std::string strSql = "ALTER TABLE ";
		strSql += name;
		strSql += " ADD COLUMN ";

		FieldSturct& field = AlertTable[uloop];
		rc_assert_continue((field.name != 0 && field.type != 0))
			CharArrayPtr FieldString = new char[FILED_SQL_LEN]();

		if (s_stricmp(field.type, FIELD_TYPE_STRING) == 0) {
			s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s varchar(%u)", field.name, field.size);
		}
		else if (s_stricmp(field.type, FIELD_TYPE_INT) == 0) {
			s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s int", field.name);
		}

		strSql += FieldString;

		//logi("sql=>%s", strSql.c_str());
		ExeCommand(strSql.c_str());
	}
	return S_OK;
}
std_method_impl CLogDataBaseImpl::CreateTable(LPCSTR name,
	ITableStruct *pITableStruct)
{
	//logi("create==>");
	
	HRESULT hr = S_OK;

	Table table;
	UINT uFieldCount = 0;

	pITableStruct->GetFieldsCount(uFieldCount);
	rc_assert_log(uFieldCount > 0, E_FAIL, "Table_Field_Zero")

	table.resize(uFieldCount);

	hr = pITableStruct->GetAllRecord(&table[0], uFieldCount);
	rc_assert(hr == S_OK, E_FAIL)

	string strSql = "CREATE TABLE IF NOT EXISTS ";
	strSql += name;
	strSql += "(";

	strSql += FILED_ID;
	strSql += " ";
	strSql += "INTEGER PRIMARY KEY NOT NULL,";

	strSql += FILED_S_TIME;
	strSql += " ";
	strSql += "INTEGER,";

	strSql += FILED_L_TIME;
	strSql += " ";
	strSql += "INTEGER,";

	UINT uloop = 0;

	for (uloop = 0; uloop < uFieldCount; uloop++)
	{
		FieldSturct& field = table[uloop];
		rc_assert_continue((field.name != 0 && field.type != 0))

		CharArrayPtr FieldString = new char[FILED_SQL_LEN]();

		if (s_stricmp(field.type, FIELD_TYPE_STRING) == 0) {
			s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s varchar(%u)", field.name, field.size);
		}
		else if (s_stricmp(field.type, FIELD_TYPE_INT) == 0) {
			s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s int", field.name);
		}
	
		strSql += FieldString;

		if (uloop == uFieldCount - 1) {
			strSql += " ";
		} else {
			strSql += ", ";
		}
	}

	strSql += ")";
	

	//logi("sql=>%s", strSql.c_str());
	ExeCommand(strSql.c_str());
	return S_OK;
}

std_method_impl CLogDataBaseImpl::Insert(LPCSTR name, cJSON* pRoot,
	ITableStruct *pITableStruct)
{

	HRESULT hr = S_OK;

	Table table;
	hr = GetTableFields(table, pITableStruct);
	rc_assert(hr == S_OK, E_FAIL)

	ULONG uFieldCount = table.size();


	string strSql;

	strSql = "INSERT INTO";
	strSql += " ";
	strSql += name;
	strSql += "(";

	strSql += FILED_S_TIME;
	strSql += ",";
	strSql += FILED_L_TIME;
	strSql += ",";

	UINT uloop = 0;
	for (uloop = 0; uloop < uFieldCount; uloop++)
	{
		strSql += table[uloop].name;
		if (uFieldCount - 1 != uloop)
			strSql += ",";
	}
	strSql += ")";
	strSql += " ";
	strSql += "VALUES(";
	strSql += "?,?,";

	for (uloop = 0; uloop < uFieldCount; uloop++)
	{
		strSql += "?";
		if (uFieldCount - 1 != uloop)
			strSql += ",";
	}
	strSql += ")";


	vector<varaint_t*> VecParams;

	for (uloop = 0; uloop < uFieldCount; uloop++)
	{
		cJSON* pJsonItem = cJSON_GetObjectItem(pRoot, table[uloop].name);

		if (s_stricmp(table[uloop].type, FIELD_TYPE_STRING) == 0) {

			if (IsJsonString(pJsonItem))
			{
				varaint_t* var = ALLOC_NEW varaint_t;
				rc_assert_continue(var != NULL)
				varaint_t_init(var);
				var->vt = VET_LPSTR;
				varaint_t_malloc_string(var, s_strlen(pJsonItem->valuestring) + 1);

				s_strcpy(var->pszVal,s_strlen(pJsonItem->valuestring), pJsonItem->valuestring);
				VecParams.push_back(var);
			}
		}
		else if (s_stricmp(table[uloop].type, FIELD_TYPE_INT) == 0) {

			if (IsJsonNumber(pJsonItem))
			{
				varaint_t* var = ALLOC_NEW varaint_t;
				rc_assert_continue(var != NULL)
				varaint_t_init(var);
				var->vt = VET_UI4;
				var->ulVal = pJsonItem->valueint;
				VecParams.push_back(var);
			}
		}
	}


	time_t tm = time(NULL);
	m_db.InsertSql(strSql.c_str(), VecParams, table, tm, tm);

	vector<varaint_t*>::iterator it = VecParams.begin();
	for (; it != VecParams.end(); )
	{
		if ((*it)->vt == VET_LPSTR)
		{
			varaint_t_free_string((*it));
		}
		delete *it;
		++it;
	}

	return S_OK;
}

std_method_impl CLogDataBaseImpl::Update(LPCSTR name, cJSON* pRoot,
	ITableStruct *pITableStruct)
{
	HRESULT hr = S_OK;

	Table table;
	hr = GetTableFields(table, pITableStruct);
	rc_assert(hr == S_OK, E_FAIL)

	ULONG uFieldCount = table.size();

	string strSql;

	strSql = "UPDATE ";
	strSql += name;
	strSql += " ";
	strSql += "SET ";

	UINT uloop = 0;

	for (uloop = 0; uloop < uFieldCount; uloop++)
	{
		cJSON* pJsonItem = cJSON_GetObjectItem(pRoot, table[uloop].name);

		rc_assert_continue(table[uloop].index == 0)

		CharArrayPtr FieldString = new char[FILED_SQL_LEN]();

		if (s_stricmp(table[uloop].type, FIELD_TYPE_STRING) == 0) 
		{
			if (IsJsonString(pJsonItem))
			{
				s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s='%s'", table[uloop].name,pJsonItem->valuestring);
				strSql += FieldString;
				strSql += ",";
			}
		}
		else if(s_stricmp(table[uloop].type, FIELD_TYPE_INT) == 0)
		{
			if (IsJsonNumber(pJsonItem))
			{
				s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s=%d", table[uloop].name, pJsonItem->valueint);
				strSql += FieldString;
				strSql += ",";
			}
		}
	}


	if (strSql[strSql.length() - 1] == ',')
	{
		strSql.erase(strSql.end() - 1);
	}
	
	strSql += " WHERE ";

	for (uloop = 0; uloop < uFieldCount; uloop++)
	{
		cJSON* pJsonItem = cJSON_GetObjectItem(pRoot, table[uloop].name);

		rc_assert_continue(table[uloop].index == 1)

		CharArrayPtr FieldString = new char[FILED_SQL_LEN]();

		if (s_stricmp(table[uloop].type, FIELD_TYPE_STRING) == 0)
		{
			if (IsJsonString(pJsonItem))
			{
				s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s='%s'", table[uloop].name, pJsonItem->valuestring);
				strSql += FieldString;
				strSql += " AND ";
			}
		}
		else if (s_stricmp(table[uloop].type, FIELD_TYPE_INT) == 0)
		{
			if (IsJsonNumber(pJsonItem))
			{
				s_strlcatf(FieldString, FILED_SQL_LEN - 1, "%s=%d", table[uloop].name, pJsonItem->valueint);
				strSql += FieldString;
				strSql += " AND ";
			}
		}
	}


	int pos = strSql.find_last_of(" AND ");
	if (pos )
	{
		int len = strSql.length();
		len = len - s_strlen(" AND ");
		strSql = strSql.substr(0, len);
	}


	ExeCommand(strSql.c_str());

	return S_OK;
}

std_method_impl CLogDataBaseImpl::Refresh(LPCSTR name, cJSON* pRoot,
	ITableStruct *pITableStruct)
{
	HRESULT hr = S_OK;
	Table table;
	hr = GetTableFields(table, pITableStruct);
	rc_assert(hr == S_OK, E_FAIL)

	ULONG uFieldCount = table.size();

	string strSql;

	strSql = "DELETE FROM";
	strSql += " ";
	strSql += name;

	return 	Insert(name, pRoot, pITableStruct);
}


std_method_impl CLogDataBaseImpl::GetTableFields(Table& table,
	ITableStruct *pITableStruct)
{
	HRESULT hr = S_OK;

	UINT uFieldCount = 0;

	pITableStruct->GetFieldsCount(uFieldCount);
	rc_assert_log(uFieldCount > 0, E_FAIL, "Table_Field_Zero")

	table.resize(uFieldCount);

	hr = pITableStruct->GetAllRecord(&table[0], uFieldCount);
	rc_assert(hr == S_OK, E_FAIL)

	return S_OK;
}

std_method_impl CLogDataBaseImpl::ExeCommand(LPCSTR cmd)
{
	m_db.ExeSql(cmd);
	return S_OK;
}
